package net.ufrog.common.app;

import net.ufrog.common.Logger;
import net.ufrog.common.converters.Converter;
import net.ufrog.common.exception.ServiceException;
import net.ufrog.common.utils.Files;
import net.ufrog.common.utils.Strings;
import org.yaml.snakeyaml.Yaml;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * @author ultrafrog, ufrog.net@gmail.com
 * @version 0.1, 2017-07-06
 * @since 0.1
 */
public class YamlWrapper {

    private static final String DEFAULT_SEPARATOR = ".";

    /** 处理工具 */
    private Yaml yaml;

    /** 属性 */
    private Object obj;

    /** 构造函数 */
    private YamlWrapper() {
        yaml = new Yaml();
    }

    /**
     * 读取属性
     *
     * @param key 键值
     * @param separator 分隔符
     * @return 属性
     */
    public Object get(String key, String separator) {
        return get(key, separator, obj);
    }

    /**
     * 读取属性
     *
     * @param key 键值
     * @return 属性
     */
    public Object get(String key) {
        return get(key, DEFAULT_SEPARATOR);
    }

    /**
     * 读取属性
     *
     * @param key 键值
     * @param separator 分隔符
     * @param needPrefix 是否需要前缀
     * @return 属性
     */
    public Properties getProperties(String key, String separator, Boolean needPrefix) {
        Object obj = get(key, separator);
        return (obj == null) ? null : getProperties(needPrefix ? key : null, separator, obj, new Properties());
    }

    /**
     * 读取属性
     *
     * @param key 键值
     * @param separator 分隔符
     * @return 属性
     */
    public Properties getProperties(String key, String separator) {
        return getProperties(key, separator, Boolean.FALSE);
    }

    /**
     * 读取属性
     *
     * @param key 键值
     * @return 属性
     */
    public Properties getProperties(String key) {
        return getProperties(key, DEFAULT_SEPARATOR);
    }

    /**
     * 读取属性映射
     *
     * @param key 键值
     * @param separator 分隔符
     * @param needPrefix 是否需要前缀
     * @return 属性映射
     */
    public Map<String, Object> getMap(String key, String separator, Boolean needPrefix) {
        Object obj = get(key, separator);
        return (obj == null) ? null : getMap(needPrefix ? key : null, separator, obj, new HashMap<>());
    }

    /**
     * 读取属性映射
     *
     * @param key 键值
     * @param separator 分隔符
     * @return 属性映射
     */
    public Map<String, Object> getMap(String key, String separator) {
        return getMap(key, separator, Boolean.FALSE);
    }

    /**
     * 读取属性映射
     *
     * @param key 键值
     * @return 属性映射
     */
    public Map<String, Object> getMap(String key) {
        return getMap(key, DEFAULT_SEPARATOR);
    }

    /**
     * 读取属性对象
     *
     * @param key 键值
     * @param separator 分隔符
     * @param requestType 请求类型
     * @param <T> 请求范型
     * @return 属性对象
     */
    public <T> T getBean(String key, String separator, Class<T> requestType) {
        try {
            T result = requestType.newInstance();
            Object obj = get(key, separator, this.obj);
            PropertiesBeans.getPropertiesValues(requestType).forEach((field, pv) -> {
                try {
                    Object value = Converter.convert(get(pv.value(), separator, obj), pv.defaultValue(), field.getType());
                    if (!field.isAccessible()) field.setAccessible(true);
                    field.set(result, value);
                } catch (Exception e) {
                    throw new ServiceException(e.getMessage(), e);
                }
            });
            return result;
        } catch (Exception e) {
            throw new ServiceException(e.getMessage(), e);
        }
    }

    /**
     * 读取属性对象
     *
     * @param key 键值
     * @param requestType 请求类型
     * @param <T> 请求范型
     * @return 属性对象
     */
    public <T> T getBean(String key, Class<T> requestType) {
        return getBean(key, DEFAULT_SEPARATOR, requestType);
    }

    /**
     * 读取属性
     *
     * @param key 键值
     * @param separator 分隔符
     * @param obj 属性
     * @return 结果属性
     */
    private Object get(String key, String separator, Object obj) {
        if (Strings.empty(key)) {
            return obj;
        } else {
            Integer idx = key.indexOf(separator);
            String first = (idx > 0) ? key.substring(0, idx) : key;
            String remain = (idx > 0) ? key.substring(idx + separator.length()) : null;

            if (obj instanceof Map) {
                @SuppressWarnings("unchecked") Map<String, ?> map = Map.class.cast(obj);
                if (map.containsKey(first)) {
                    return get(remain, separator, map.get(first));
                }
            }
            Logger.debug("cannot find properties by key: %s", key);
            return null;
        }
    }

    /**
     * 读取属性
     *
     * @param key 键值
     * @param separator 分隔符
     * @param obj 属性
     * @param properties 结果属性
     * @return 属性
     */
    @SuppressWarnings("unchecked")
    private Properties getProperties(String key, String separator, Object obj, Properties properties) {
        if (obj instanceof Map) {
            Map.class.cast(obj).forEach((k, v) -> getProperties((Strings.empty(key) ? "" : key + separator) + k, separator, v, properties));
        } else if (obj != null) {
            properties.put(key, obj);
        }
        return properties;
    }

    /**
     * 读取属性映射
     *
     * @param key 键值
     * @param separator 分隔符
     * @param obj 属性
     * @param map 映射
     * @return 属性映射
     */
    @SuppressWarnings("unchecked")
    private Map<String, Object> getMap(String key, String separator, Object obj, Map<String, Object> map) {
        if (obj instanceof Map) {
            Map.class.cast(obj).forEach((k, v) -> getMap((Strings.empty(key) ? "" : key + separator) + k, separator, v, map));
        } else {
            map.put(key, obj);
        }
        return map;
    }

    /**
     * 创建实例
     *
     * @param inputStream 输入流
     * @return 配置封装
     */
    public static YamlWrapper create(InputStream inputStream) {
        YamlWrapper yamlWrapper = new YamlWrapper();
        yamlWrapper.obj = yamlWrapper.yaml.load(inputStream);
        return yamlWrapper;
    }

    /**
     * 创建实例
     *
     * @param path 文件路径
     * @return 配置封装
     */
    public static YamlWrapper create(String path) {
        try (InputStream inputStream = Files.getInputStream(path)) {
            return create(inputStream);
        } catch (IOException e) {
            throw new ServiceException(e.getMessage(), e);
        }
    }
}
